package indi.zn.commons.serialno;



import java.util.Date;
import java.util.LinkedList;
import java.util.Queue;
import java.util.UUID;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.google.common.base.Strings;

/**  
*
*  
* @author 周楠
* @Description: 
* @date 2017年10月13日
*
*/
@Table(name="base_sequence") 
public class Sequence {
	
	 @Id
	 @Column(name="id",nullable=false)
	 private String id;
	 
	 @Column(name="seq_name")
 	 private String name;
	 
	 @Column(name="current")
	 private Long current;
	 
	 @Column(name="`begin`")
	 private Long begin=0l;
	 
	 @Column(name="step")
	 private Integer step;
	 
	 @Column(name="`range`")
	 private Long range;
	 
	 private int rlength;
	 
	 @Column(name="cycle")
	 private boolean cycle;
	 
	 @Transient
	 private boolean zeroBlank=true;
	 
	 @Column(name="cashe_size")
     private Integer casheSize=50;
     
     @Transient
	 private  Queue<Long> cashe;
     @Transient
	 private SeqPersistenceService  persister;
	 
     @Column(name="create_time")
     private Date createTime;
     
	 /**
	 * @param current
	 * @param begin
	 * @param step
	 * @param range
	 * @param cycle
	 */
	 
	 /**
	 * 
	 */
	public Sequence() {
		// TODO Auto-generated constructor stub
	}
	 
	public Sequence(String name,Long begin, Integer step, Long range, boolean cycle ,SeqPersistenceService  persister,String dialect) {
		super();
		this.name = name;
		this.current = begin;
		this.begin = begin;
		this.step = step;
		this.range = range;
		this.cycle = cycle;
		this.casheSize=20;
		cashe=new LinkedList<>();
		this.persister=persister;
		build(dialect);
		
		rlength=range.toString().length()-1;
		
	}
	
	/**
	 * @param name
	 * @param current
	 * @param begin
	 * @param step
	 * @param range
	 * @param cycle
	 * @param casheSize
	 */
	
	public Sequence(String name,Long begin, Integer step, Long range, boolean cycle,boolean zeroBlank, Integer casheSize,SeqPersistenceService  persister,String dialect) {
		super();
		this.zeroBlank=zeroBlank;
		this.name = name;
		this.current = begin;
		this.begin=begin;
		this.step = step;
		this.range = range;
		this.cycle = cycle;
		this.casheSize = casheSize;
		if(casheSize!=null&&casheSize>0) {	
			cashe=new LinkedList<>();
		}
		this.persister=persister;
		build(dialect);
		
		rlength=range.toString().length();
		
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getStep() {
		return step;
	}

	public void setStep(Integer step) {
		this.step = step;
	}

	public Long getRange() {
		return range;
	}

	public void setRange(Long range) {
		this.range = range;
	}

	public Integer getCasheSize() {
		return casheSize;
	}

	public void setCasheSize(Integer casheSize) {
		this.casheSize = casheSize;
	}
    public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}
	public SeqPersistenceService getPersister() {
		return persister;
	}

	public void setPersister(SeqPersistenceService persister) {
		this.persister = persister;
	}
	public boolean isZeroBlank() {
		return zeroBlank;
	}

	public void setZeroBlank(boolean zeroBlank) {
		this.zeroBlank = zeroBlank;
	}

	private void build(String dialect) {

    	 if(persister!=null) {
    		 
    		 Sequence seq= persister.regress(name);
    		
    		 if(seq==null) {
    			  			 
    			 this.id=UUID.randomUUID().toString().toLowerCase().replace("-", "");  //!WARNING
    			
    			 if(dialect==null) {
    			 persister.create(this);
    			 }
    			 else if(dialect!=null&&dialect.equals("mysql")) {
    				 persister.createMysql(this); 
    			 }
    			 
    			 
    		 }else {
    			 
    			seq.setPersister(persister);
    			this.name = seq.name;
    			this.current =  seq.current;
    			this.begin =  seq.begin;
    			this.step =  seq.step;
    			this.range =  seq.range;
    			this.cycle =  seq.cycle;
    			this.casheSize=seq.casheSize;
    			 
    		 }
    		  
    		  
    	 }
    	 
     }
	
	
	
	private  String blankZero(String numstr) {
		
		if(numstr.length()<rlength) {
			
			 int i=rlength-numstr.length();
			
			 for (int j = 0; j <i; j++) {
				 numstr=0+numstr;
			}

	}
		return numstr;
		
	}

	public synchronized String getNext(){
		 
		String numstr="";
		
		if(casheSize!=null&&casheSize>0){
		
			numstr+=cashedNext();
		}
		else {
			
			numstr+=next();

		}
		
		
		if(Strings.isNullOrEmpty(numstr)) {
			numstr="0";
		}
		
		if(zeroBlank) {
			numstr=blankZero(numstr);
		}

		return numstr;
		
	}
	
	private Long cashedNext() {
		
   		  if(cashe.size()==0) {
   			  cashed();
   		  }
   		  
   		 return cashe.poll();
   		 
	}
	
	
	@Transient
	private void cashed() {
		
		 for (int i = 0; i < casheSize; i++) {
			
			 current=current+step;
	    		
	    		if(cycle&&range!=null&&range<=current){
	    			
	    			current=current-range;	
	    		}
	    		
			 cashe.add(current);
			 
		}  
		 
		 if(persister!=null) {
		 persister.merge(this);
		 }
		 
	}
	

     private Long next() {
    	 
    	 current=current+step;
    		
    		if(cycle&&range!=null&&range<=current){
    			
    			current=current-range;	
    		}
    		
    		return current;
    		
    	}
     
     
     
     public synchronized void reset() {
    	 
    	 current=begin;
    	 cashe.clear();
    	 
     }
     
     
    

}
